package de.eeb.common;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.json.JSONArray;
import org.json.JSONObject;

import android.util.Log;

public class SBWSController {
    
    private static String BASE_URL = "http://manae.de/sb.php?";
    
    public Boolean registerUser(String number, String name, String email) throws SBException {
        try {
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "name", "email", "number"};
            String[] values = new String[]{"shared_bucks", "registerUser", name, email, hash};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            String content = doHTTPGETToString(url);
            JSONObject o = new JSONObject(content);
            return o.getBoolean("result");
        } catch (Exception e) {
            throw new SBException(e);
        }
    }
    
    public Boolean isUser(String number) throws SBException {
        try {
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "number"};
            String[] values = new String[]{"shared_bucks", "isUser", hash};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            String content = doHTTPGETToString(url);
            JSONObject o = new JSONObject(content);
            return o.getBoolean("result");
        } catch (Exception e) {
            throw new SBException(e);
        }        
    }
    
    // "date": "2012-06-11 17:51:15",
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
    
    public List<SBPayment> getPaymentsFor(String number) throws SBException {
        try {
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "number"};
            String[] values = new String[]{"shared_bucks", "getPaymentsFor", hash};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            String content = doHTTPGETToString(url);
            JSONObject o = new JSONObject(content);
            JSONObject resultObject = o.getJSONObject("result");
            JSONArray paymentArray = resultObject.getJSONArray("payments");
            
            List<SBPayment> paymentList = new ArrayList<SBPayment>();
            
            for (int i = 0; i < paymentArray.length(); i++) {
            	JSONObject payment = paymentArray.getJSONObject(i).getJSONObject("payment");
            	String id = payment.getString("id");
            	String desc = payment.getString("description");
            	String date = payment.getString("date");
            	Double amount = payment.getDouble("amount");
            	String buyer = payment.getString("from");
            	Date d = df.parse(date);
            	SBPayment sbPayment = new SBPayment(buyer, id, amount, desc, d, false, null);
				paymentList.add(sbPayment);
			}
            
            return paymentList;
        } catch (Exception e) {
            throw new SBException(e);
        }        	
    }
    
    public Integer makePayment(String number, Double amount, String desc) throws SBException {
        try {
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "number", "amount", "desc"};
            String[] values = new String[]{"shared_bucks", "makePayment", hash, amount.toString(), desc};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            String content = doHTTPGETToString(url);
            JSONObject o = new JSONObject(content);
            return o.getInt("result");
            
        } catch (Exception e) {
            throw new SBException(e);
        }        
    }
    
    public Boolean participate(String paymentId, String number) throws SBException {
        try {
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "number", "payment"};
            String[] values = new String[]{"shared_bucks", "participate", hash, paymentId};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            String content = doHTTPGETToString(url);
            JSONObject o = new JSONObject(content);
            return o.getBoolean("result");
        } catch (Exception e) {
            throw new SBException(e);
        }        
    }    
    
    public List<String> getFellowUsers(List<String> fellowNumbers) throws SBException {
        try {
            String[] keys = new String[]{"access", "action",};
            String[] values = new String[]{"shared_bucks", "checkFellowUsers"};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");

            // Generate MD5 hash of each number
            List<String> hashList = new ArrayList<String>();
            for (String number : fellowNumbers) {
                hashList.add(hashAsMd5(number));
            }

            // Generate JSon object		
            JSONArray array = new JSONArray(hashList);
            
            String content = doHTTPPOSTToString(url, "result", array);
            
            Log.v(this.getClass().getName(), content);

            // Get Number array
            JSONObject o = new JSONObject(content);
            JSONArray resultArray = o.getJSONArray("result");

            // Return List with fellow users
            List<String> users = new ArrayList<String>(resultArray.length());
            for (int i = 0; i < resultArray.length(); i++) {
            	JSONObject jo = resultArray.getJSONObject(i);
            	
                users.add(jo.getString("number"));
            }
            
            return users;
        } catch (Exception e) {
            throw new SBException(e);
        }        
    }
    
    public Set<SBPayment> invoice(String number) throws SBException {
        try {
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "number"};
            String[] values = new String[]{"shared_bucks", "invoice", hash};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            
            String content = doHTTPGETToString(url);
            
            JSONObject o = new JSONObject(content);
            JSONObject resultObject = o.getJSONObject("result");
            JSONArray paymentArray = resultObject.getJSONArray("payments");
            
            Map<String, SBPayment> paymentMap = new HashMap<String, SBPayment>();

            // Parse Payments array
            for (int i = 0; i < paymentArray.length(); i++) {
                JSONObject jsonPayment = paymentArray.getJSONObject(i).getJSONObject("payment");
                String id = jsonPayment.getString("id");
                String participant = jsonPayment.getString("participant");
                
                SBPayment p = null;
                
                if (paymentMap.containsKey(id)) {
                    p = paymentMap.get(id);
                } else {
                    p = new SBPayment(id);
                    p.setAmount(jsonPayment.getDouble("amount"));
                    p.setClosed(jsonPayment.getInt("closed") == 0 ? false : true);
                    p.setDate(new Date(jsonPayment.getLong("timestamp")));
                    p.setDescription(jsonPayment.getString("description"));
                    
                    paymentMap.put(id, p);
                }
                
                p.getParticipants().add(participant);
            }

            // Return Set<SBPayment>
            Set<SBPayment> paymentSet = new HashSet<SBPayment>();
            for (String key : paymentMap.keySet()) {
                paymentSet.add(paymentMap.get(key));
            }
            
            return paymentSet;
            
        } catch (Exception e) {
            throw new SBException(e);
        }        
    }
    
    public Set<SBDebt> getDebts(String number) throws SBException {
        try {
            Set<SBDebt> debtSet = new HashSet<SBDebt>();
            String hash = hashAsMd5(number);
            String[] keys = new String[]{"access", "action", "number"};
            String[] values = new String[]{"shared_bucks", "getDebtNow", hash};
            List<NameValuePair> l = buildParameterList(keys, values);
            String url = BASE_URL + URLEncodedUtils.format(l, "UTF-8");
            
            String content = doHTTPGETToString(url);
            
            JSONObject o = new JSONObject(content);
            JSONObject resultObject = o.getJSONObject("result");
            JSONArray debtArray = resultObject.getJSONArray("debts");

            for (int i = 0; i < debtArray.length(); i++) {
                JSONObject jsonDebt = debtArray.getJSONObject(i).getJSONObject("debt");
                String n = jsonDebt.getString("number");
                Double a = jsonDebt.getDouble("amount");
                SBDebt d = new SBDebt(n, a);
                debtSet.add(d);
            }
            
            
            return debtSet;
            
        } catch (Exception e) {
            throw new SBException(e);
        }        
        
        
    }
    
    public static String hashAsMd5(String input) throws NoSuchAlgorithmException {        
        String result = input;
        if (input != null) {
            MessageDigest md = MessageDigest.getInstance("MD5"); //or "SHA-1"
            md.update(input.getBytes());
            BigInteger hash = new BigInteger(1, md.digest());
            result = hash.toString(16);
            while (result.length() < 32) {
                result = "0" + result;
            }
        }
        return result;        
    }
    
    private static List<NameValuePair> buildParameterList(String[] keys, String[] values) {
        assert (keys.length == values.length);
        List<NameValuePair> list = new ArrayList<NameValuePair>();
        for (int i = 0; i < keys.length; i++) {
            NameValuePair p1 = new BasicNameValuePair(keys[i], values[i]);
            list.add(p1);
        }
        return list;
    }
    
    private String doHTTPPOSTToString(String url, String param, Object o) throws SBException {        
        try {
            StringBuilder builder = new StringBuilder();
            HttpParams params = new BasicHttpParams();
            params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpClient client = new DefaultHttpClient(params);
            HttpPost httpPost = new HttpPost(url);

            // Add POST variables
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
            nameValuePairs.add(new BasicNameValuePair("result", o.toString()));
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
            
            HttpResponse response = client.execute(httpPost);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode == 200) {
                HttpEntity entity = response.getEntity();
                InputStream content = entity.getContent();
                BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(line);
                }
            } else {
                throw new SBException("Cannot to connect/download via HTTP GET");
            }
            return builder.toString();
        } catch (Exception e) {
            throw new SBException(e);
        }        
        
    }
    
    private String doHTTPGETToString(String url) throws SBException {
        try {
            StringBuilder builder = new StringBuilder();
            HttpClient client = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            
            HttpResponse response = client.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode == 200) {
                HttpEntity entity = response.getEntity();
                InputStream content = entity.getContent();
                BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(line);
                }
            } else {
                throw new SBException("Cannot to connect/download via HTTP GET");
            }
            return builder.toString();
        } catch (Exception e) {
            throw new SBException(e);
        }
    }
}
